﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Base;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Web
{
    /// <summary>
    /// http服务器类
    /// </summary>
    public class HttpServer : IDisposable
    {
        /// <summary>
        /// 请求处理对象
        /// </summary>
        private readonly IHttpRequestHandler _requestHandler;

        private readonly HttpServerConfig _serverConfig;
        /// <summary>
        /// 服务器配置
        /// </summary>
        public HttpServerConfig ServerConfig
        {
            get { return _serverConfig; }
        }


        /// <summary>
        /// 服务端监听器
        /// </summary>
        private readonly NetTcpListener _serverListener;

        /// <summary>
        /// http连接列表
        /// </summary>
        private readonly List<HttpConnection> _httpConnectionList = new List<HttpConnection>();

        /// <summary>
        /// 清理过期的HttpConnection线程
        /// </summary>
        private readonly ThreadEx _clearExpireHttpConnectionThread;



        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="serverConfig">http服务器配置</param>
        /// <param name="requestHandler">http请求处理对象</param>
        /// <exception cref="ArgumentNullException">参数不能为空，否则将抛出异常</exception>
        public HttpServer(HttpServerConfig serverConfig, IHttpRequestHandler requestHandler)
        {
            if (serverConfig == null)
            {
                throw new ArgumentNullException(nameof(serverConfig));
            }

            if (requestHandler == null)
            {
                throw new ArgumentNullException(nameof(requestHandler));
            }

            var listenEndPoint = serverConfig.ListenEndPoint;
            if (listenEndPoint == null)
            {
                throw new ArgumentNullException(nameof(listenEndPoint));
            }
            string listenEndPointStr = NetHelper.GetIPPortString(listenEndPoint);

            this._serverConfig = serverConfig;
            this._requestHandler = requestHandler;

            var initPara = new NetTcpListenerInitPara(listenEndPoint, serverConfig.Backlog)
            {
                MaxConnections = serverConfig.MaxConnections
            };
            //initPara.AcceptMode = ThreadMode.Sync;
            //initPara.SyncThreadName = $"BMQ{NetHelper.GetIPPortString(config.BindEndPoint)}监听线程";
            this._serverListener = new NetTcpListener(this.AcceptSocketCallback, initPara);

            this._clearExpireHttpConnectionThread = new ThreadEx(this.ClearExpireHttpConnectionThreadMethod, $"http监听{listenEndPointStr}过期{nameof(HttpConnection)}清理线程");
        }

        private void ClearExpireHttpConnectionThreadMethod(ThreadExPara threadPara)
        {
            try
            {
                int httpConnectionExpireMilliseconds = this._serverConfig.HttpConnectionExpireMilliseconds;
                int intervalMilliseconds = httpConnectionExpireMilliseconds / 4;//取过期时间的的1/4作为检查间隔
                long currentTimestamp;
                int index;

                while (!threadPara.Token.IsCancellationRequested)
                {
                    try
                    {
                        if (threadPara.WaitOne(intervalMilliseconds))
                        {
                            continue;
                        }

                        if (this._httpConnectionList.Count == 0)
                        {
                            continue;
                        }

                        currentTimestamp = TimeEx.GetTimestamp();
                        index = 0;
                        while (index < this._httpConnectionList.Count)
                        {
                            HttpConnection httpConnection = this._httpConnectionList[index];
                            if (httpConnection.IsExpire(currentTimestamp, httpConnectionExpireMilliseconds))
                            {
                                httpConnection.Dispose();
                                continue;
                            }

                            index++;
                        }
                    }

                    catch (Exception exi)
                    {
                        ZLoger.Error(exi);
                    }
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }


        private void AcceptSocketCallback(AcceptSocketArgs args)
        {
            try
            {
                var httpConnection = new HttpConnection(this, args, this._requestHandler);
                this.AddHttpConnection(httpConnection);
            }
            catch (ObjectDisposedException)
            { }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }


        private void AddHttpConnection(HttpConnection httpConnection)
        {
            lock (this._httpConnectionList)
            {
                this._httpConnectionList.Add(httpConnection);
            }
        }

        internal void RemoveHttpConnection(HttpConnection httpConnection)
        {
            lock (this._httpConnectionList)
            {
                this._httpConnectionList.Remove(httpConnection);
            }
        }



        /// <summary>
        /// 启动web服务器
        /// </summary>
        public void Start()
        {
            if (this._serverListener.Accepting)
            {
                return;
            }

            this._serverListener.Start();

            string httpProtocol = this._serverConfig.Certificate == null ? "http" : "https";
            var listenEndPoint = this._serverConfig.ListenEndPoint;
            ZLoger.Debug($"HttpSever is running at {httpProtocol}://{listenEndPoint.Address.ToString()}:{listenEndPoint.Port}");

            this._clearExpireHttpConnectionThread.Start();
        }

        /// <summary>
        /// 停止web服务器
        /// </summary>
        public void Stop()
        {
            this._serverListener.Stop();
        }


        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            this._serverListener.Stop();
            this._serverListener.Dispose();

            this._clearExpireHttpConnectionThread.Stop();
            this._clearExpireHttpConnectionThread.Dispose();

            HttpConnection[] connections;
            lock (this._httpConnectionList)
            {
                connections = this._httpConnectionList.ToArray();
                this._httpConnectionList.Clear();
            }

            foreach (var con in connections)
            {
                con.Dispose();
            }
        }
    }
}
